﻿using UnityEngine;
using System.Collections;

public class RemainInBounds : MonoBehaviour {

    /// Camera behavior to keep it within the bounds of a set of colliders.
    /// Each frame, we check to see which corners are touching the outsides
    /// of the perimeter, and determine what direction (if any) we can move
    /// the camera to get it out of there.

    public float adjustmentValue = 0.1f;

    public enum BoundingMode
    {
        AdjustWithSlide,
        DenyInvalidMove
    }

    public BoundingMode boundingMode = BoundingMode.AdjustWithSlide;

    public Vector3 desiredPosition = Vector3.zero;

	// Use this for initialization
	void Start () {
        desiredPosition = transform.position;
	}
	
	// Update is called once per frame
	void Update () {
	
	}

    void LateUpdate()
    {
        switch (boundingMode)
        {
            case BoundingMode.AdjustWithSlide:
                DoAdjustWithSlide();
                break;
            case BoundingMode.DenyInvalidMove:
                DoDenyInvalidMove();
                break;
        }



    }

    private void GetBoundingState(Vector3 position, out bool topLeftOut, out bool topRightOut, out bool bottomLeftOut, out bool bottomRightOut)
    {
        Ray topLeftRay = camera.ScreenPointToRay(new Vector3(0, 0, position.z));
        Ray topRightRay = camera.ScreenPointToRay(new Vector3(Screen.width - 1, 0, position.z));
        Ray bottomLeftRay = camera.ScreenPointToRay(new Vector3(0, Screen.height - 1, position.z));
        Ray bottomRightRay = camera.ScreenPointToRay(new Vector3(Screen.width - 1, Screen.height - 1, position.z));

        int layerMask = 1 << 9;

        topLeftOut = Physics.Raycast(topLeftRay, 1000, layerMask);
        topRightOut = Physics.Raycast(topRightRay, 1000, layerMask);
        bottomLeftOut = Physics.Raycast(bottomLeftRay, 1000, layerMask);
        bottomRightOut = Physics.Raycast(bottomRightRay, 1000, layerMask);
    }

    private void DoDenyInvalidMove()
    {
        bool topLeftOutOfBounds, topRightOutOfBounds, bottomLeftOutOfBounds, bottomRightOutOfBounds;
        GetBoundingState(desiredPosition, out topLeftOutOfBounds, out topRightOutOfBounds, out bottomLeftOutOfBounds, out bottomRightOutOfBounds);

        if (topLeftOutOfBounds || topRightOutOfBounds || bottomLeftOutOfBounds || bottomRightOutOfBounds) return;

        this.transform.position = desiredPosition;
    }

    private void DoAdjustWithSlide()
    {
        bool topLeftOutOfBounds, topRightOutOfBounds, bottomLeftOutOfBounds, bottomRightOutOfBounds;
        GetBoundingState(desiredPosition, out topLeftOutOfBounds, out topRightOutOfBounds, out bottomLeftOutOfBounds, out bottomRightOutOfBounds);

        bool leftOutOfBounds = topLeftOutOfBounds && bottomLeftOutOfBounds;
        bool rightOutOfBounds = topRightOutOfBounds && bottomRightOutOfBounds;
        bool topOutOfBounds = topLeftOutOfBounds && topRightOutOfBounds;
        bool bottomOutOfBounds = bottomLeftOutOfBounds && bottomRightOutOfBounds;

        Vector3 adjustPosition = Vector3.zero;

        if (leftOutOfBounds && !rightOutOfBounds) adjustPosition.x += adjustmentValue;
        if (rightOutOfBounds && !leftOutOfBounds) adjustPosition.x -= adjustmentValue;
        if (topOutOfBounds && !bottomOutOfBounds) adjustPosition.y += adjustmentValue;
        if (bottomOutOfBounds && !topOutOfBounds) adjustPosition.y -= adjustmentValue;

        transform.position = desiredPosition + adjustPosition;
    }
}
